//
// $Id$

package com.chuhay.elemwar;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;

import static com.chuhay.elemwar.ElemWarConstants.*;

import com.samskivert.swing.Label;
import com.threerings.media.VirtualMediaPanel;
import com.threerings.media.animation.FloatingTextAnimation;

import com.threerings.parlor.media.ScoreAnimation;
import com.threerings.presents.dobj.EntryAddedEvent;
import com.threerings.presents.dobj.EntryRemovedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.SetListener;

import com.threerings.toybox.util.ToyBoxContext;

import com.threerings.crowd.client.PlaceView;
import com.threerings.crowd.data.PlaceObject;

/**
 * Displays the main game interface (the board).
 */
public class ElemWarBoardView extends VirtualMediaPanel implements PlaceView, SetListener {
   // from interface PlaceView
   public void didLeavePlace (PlaceObject plobj) {}

   /** Provides access to client services. */
   protected ToyBoxContext _ctx;

   /** A reference to our client's controller */
   protected ElemWarController _ctrl;

   /** A reference to our game object. */
   protected ElemWarObject _gameobj;

   /** The currently selected hex */
   protected HexSprite selectedHex;

   /** The background color for the board region */
   private static Color bgColor;

   /** The size of the Reversi board. */
   protected static Dimension _size;

   /** Contains a mapping from hex id to the sprite for that hex. */
   protected HashMap <Comparable, HexSprite> _hexes = new HashMap <Comparable, HexSprite>();

   /** Contains a mapping from piece id to the sprite for that piece. */
   protected HashMap <Comparable, PieceSprite> _pieces = new HashMap <Comparable, PieceSprite>();

   static {
      bgColor = new Color(90, 90, 90);
      _size = new Dimension(14, 11);
   }

   /**
    * Constructs a view which will initialize itself and prepare to display the
    * game board.
    */
   public ElemWarBoardView (ToyBoxContext ctx, ElemWarController ctrl) {
      super(ctx.getFrameManager());
      _ctx = ctx;
      _ctrl = ctrl;

      selectedHex = null;

      // listen for mouse motion and presses
      addMouseListener(new MouseAdapter() {
         public void mouseClicked (MouseEvent e) {
            _ctrl.hexClicked(e.getPoint());
         }
      });

   }

   // from MediaPanel
   @Override
   protected void paintBehind (Graphics2D gfx, Rectangle dirtyRect) {
      super.paintBehind(gfx, dirtyRect);

      // fill in our background color
      gfx.setColor(bgColor);
      gfx.fill(dirtyRect);
   }

   @Override
   // from JComponent
   public Dimension getPreferredSize () {
      return new Dimension((_size.width + 1) * 40, (int) ((_size.height + 1) * 0.75 * 40));
   }

   /**
    * Translates grid coordinates into window coordinates so the pieces and
    * pieces can be drawn correctly
    * 
    * 
    * @param x
    *           The x coordinate in the board grid
    * @param y
    *           The y coordinate in the board grid
    * @return A Point object containing the window coordinates of the center of
    *         the hex
    */
   public static Point translateCoords (int x, int y) {
      return new Point((y % 2 == 0 ? CORNERDIST : SIZE) + (SIZE) * x + 50, (int) (1.5 * (CORNERDIST) * (y + 1) + 20));
   }

   // from interface PlaceView
   public void willEnterPlace (PlaceObject plobj) {
      _gameobj = (ElemWarObject) plobj;
      _gameobj.addListener(this);

      // create sprites for all pieces currently on the board
      for (ElemWarObject.Hex hex : _gameobj._hexes) {
         addHexSprite(hex);
      }
   }

   /**
    * Adds a sprite to the board for the supplied hex.
    */
   protected void addHexSprite (ElemWarObject.Hex hex) {
      HexSprite sprite = new HexSprite(hex);
      _hexes.put(hex.getKey(), sprite);
      addSprite(sprite);
   }

   /**
    * Adds a sprite to the board for the supplied piece.
    */
   protected void addPieceSprite (ElemWarObject.Piece piece) {
      PieceSprite sprite = new PieceSprite(piece);
      _pieces.put(piece.getKey(), sprite);
      addSprite(sprite);
   }

   // from interface SetListener
   public void entryAdded (EntryAddedEvent event) {
      if (event.getName().equals(ElemWarObject._HEXES)) {
         // add a sprite for the newly created hex
         addHexSprite((ElemWarObject.Hex) event.getEntry());
      }
      if (event.getName().equals(ElemWarObject._PIECES)) {
         // add a sprite for the newly created piece
         addPieceSprite((ElemWarObject.Piece) event.getEntry());
      }
   }

   // from interface SetListener
   public void entryUpdated (EntryUpdatedEvent event) {
      if (event.getName().equals(ElemWarObject._HEXES)) {
         // update the sprite that is displaying the updated piece
         ElemWarObject.Hex hex = (ElemWarObject.Hex) event.getEntry();
         _hexes.get(hex.getKey()).updateHex(hex);
      }
      if (event.getName().equals(ElemWarObject._PIECES)) {
         // update the sprite that is displaying the updated piece
         // should only ever get here if manager moves the piece
         ElemWarObject.Piece piece = (ElemWarObject.Piece) event.getEntry();
         PieceSprite sprite = _pieces.get(piece.getKey());
         sprite.updatePiece(piece);
         _ctrl.setValidMoves(null);
      }
   }

   // from interface SetListener
   public void entryRemoved (EntryRemovedEvent event) {
   // nothing to do here
   }

   /**
    * Returns a Point containing the grid coordinates of the center of the hex
    * that was clicked in
    * 
    * Code translated from crossingstest at
    * http://www.acm.org/tog/resources/RTNews/html/rtnv5n3.html#art3
    * 
    * @param ax
    *           The x coordinate of the point being tested
    * @param ay
    *           The y coordinate of the point being tested
    * @return a Point containing the grid coordinates of the hex, returns null
    *         if not in a valid hex
    */
   protected Point getHexCenterForCoords (int ax, int ay) {
      Point center;

      for (HexSprite sprite : _hexes.values()) {
         // Points defining the hex
         center = new Point(sprite._hex.x, sprite._hex.y);
         int [] xPoints = {center.x, center.x + CORNERDIST, center.x + CORNERDIST, center.x, center.x - CORNERDIST, center.x - CORNERDIST};
         int [] yPoints = {center.y - CORNERDIST, center.y - CORNERDIST / 2, center.y + CORNERDIST / 2, center.y + CORNERDIST, center.y + CORNERDIST / 2, center.y - CORNERDIST / 2};

         int j;
         boolean xflag0;

         Point vtx0, vtx1;
         double dv0;
         int crossings;
         boolean yflag0, yflag1;

         vtx0 = new Point(xPoints[5], yPoints[5]);
         vtx1 = new Point();
         yflag0 = yflag1 = false;

         /* get test bit for above/below Y axis */
         yflag0 = (dv0 = vtx0.y - ay) >= 0.0;

         crossings = 0;
         for (j = 0; j < 6; j++) {
            /*
             * cleverness: bobble between filling endpoints of edges, so that
             * the previous edge's shared endpoint is maintained.
             */
            if ((j & 0x1) != 0) {
               vtx0.setLocation(xPoints[j], yPoints[j]);
               yflag0 = (dv0 = vtx0.y - ay) >= 0.0;
            } else {
               vtx1.setLocation(xPoints[j], yPoints[j]);
               yflag1 = (vtx1.y >= ay);
            }

            /* check if points not both above/below X axis - can't hit ray */
            if (yflag0 != yflag1) {
               /* check if points on same side of Y axis */
               if ((xflag0 = (vtx0.x >= ax)) == (vtx1.x >= ax)) {

                  if (xflag0)
                     crossings++;
               } else {
                  /*
                   * compute intersection of pgon segment with X ray, note if >
                   * point's X.
                   */
                  if ((vtx0.x - dv0 * (vtx1.x - vtx0.x) / (vtx1.y - vtx0.y)) >= ax)
                     crossings++;
               }
            }
         }
         /* test if crossings is odd */
         if ((crossings & 0x01) != 0)
            return center;
      }

      return null;
   }

   /** Convenience method for (int, int) version */
   protected Point getHexCenterForPoint (Point p) {
      return getHexCenterForCoords(p.x, p.y);
   }

   /**
    * Floats the supplied text over the board.
    */
   public void displayFloatingText (String text) {
      Label label = ScoreAnimation.createLabel(text, Color.white, new Font("Helvetica", Font.BOLD, 48), this);
      int lx = (getWidth() - label.getSize().width) / 2;
      int ly = (getHeight() - label.getSize().height) / 2;
      addAnimation(new FloatingTextAnimation(label, lx, ly));
   }
}
